﻿#region using
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;
#endregion

namespace XmlGenSharp.Logic.Main
{
  public class AdvancedEnumGenerator : GeneratorBase
  {
    // Constructors

    #region AdvancedEnumGenerator(string inputFileName, string defaultNamespace, EnumerationInfo enumInfo)
    public AdvancedEnumGenerator(string inputFileName, string defaultNamespace, EnumerationInfo enumInfo)
      : base(inputFileName, defaultNamespace)
    {
      EnumInfo = enumInfo;
    }
    #endregion

    // Public Properties

    #region EnumInfo
    public EnumerationInfo EnumInfo
    {
      get;
      protected set;
    }
    #endregion

    // Public Methods

    #region GetExtension()
    public override string GetExtension()
    {
      return ".Generator.cs";
    }
    #endregion

    #region GetFileName()
    public override string GetFileName()
    {
      string baseName = Path.GetFileNameWithoutExtension(InputFileName);

      return baseName + ".Generator.cs";
    }
    #endregion

    #region Generate(IGeneratorContext cx)
    public override void Generate(IGeneratorContext cx)
    {
      cx.W(PreText);

      cx.WL("");
      cx.WL("  public partial class {0}", EnumInfo.Name);
      cx.WL("  {");

      GenerateConstants(cx);
      GenerateConstructors(cx);
      GenerateProperties(cx);
      GenerateMethods(cx);
      GenerateStaticMethods(cx);
      GenerateOperators(cx);
      GenerateSubclasses(cx);

      cx.WL("  }");
      cx.W(PostText);
    }
    #endregion

    // Private Methods

    #region GenerateConstants(IGeneratorContext cx)
    private void GenerateConstants(IGeneratorContext cx)
    {
      cx.WL("    // Public Constants");
      cx.WL("");
      cx.WL("    #region Public Constants");

      foreach (Element element in EnumInfo.Elements)
      {
        cx.WL("    public const int {0}Val = {1};", element.Name, element.Value);
      }

      cx.WL("");
      cx.WL("    public static readonly {0} Unknown = new {0}(\"Unknown\", 0);", EnumInfo.Name);

      foreach (Element element in EnumInfo.Elements)
      {
        string ctorParams = GetConstructorParams(cx, element);
        cx.WL("    public static readonly {1}{0} {1} = new {1}{0}({2});", EnumInfo.Name, element.Name, ctorParams);
      }

      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateConstructors(IGeneratorContext cx)
    private void GenerateConstructors(IGeneratorContext cx)
    {
      cx.WL("");
      cx.WL("    // Public Constructors");

      cx.WL("");
      cx.WL("    #region {0}(string text, int val)", EnumInfo.Name);
      cx.WL("    public {0}(string text, int val)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      {0} = text;", EnumInfo.TextPropertyName);
      cx.WL("      {0} = val;", EnumInfo.ValuePropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");

      if (EnumInfo.EnumProperties.Count > 0)
      {
        string paramDecl = GetParameterDeclaration(cx);

        cx.WL("");
        cx.WL("    #region {0}({1})", EnumInfo.Name, paramDecl);
        cx.WL("    public {0}({1})", EnumInfo.Name, paramDecl);
        cx.WL("    {");
        cx.WL("      {0} = text;", EnumInfo.TextPropertyName);
        cx.WL("      {0} = val;", EnumInfo.ValuePropertyName);

        foreach (EnumProperty ep in EnumInfo.EnumProperties)
          cx.WL("      {0} = {1};", ep.Name, ToCamelCase(ep.Name));

        cx.WL("    }");
        cx.WL("    #endregion");
      }
    }
    #endregion

    #region GenerateProperties(IGeneratorContext cx)
    private void GenerateProperties(IGeneratorContext cx)
    {
      // Generate Standard Properties

      cx.WL("");
      cx.WL("    // Public Properties");
      cx.WL("");

      cx.WL("    #region {0}", EnumInfo.TextPropertyName);
      cx.WL("    public string {0}", EnumInfo.TextPropertyName);
      cx.WL("    {");
      cx.WL("      get;");
      cx.WL("      protected set;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");

      cx.WL("    #region {0}", EnumInfo.ValuePropertyName);
      cx.WL("    public int {0}", EnumInfo.ValuePropertyName);
      cx.WL("    {");
      cx.WL("      get;");
      cx.WL("      protected set;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");

      cx.WL("    #region IsUnknown");
      cx.WL("    public bool IsUnknown");
      cx.WL("    {");
      cx.WL("      get");
      cx.WL("      {");
      cx.WL("        return Value == 0;");
      cx.WL("      }");
      cx.WL("    }");
      cx.WL("    #endregion");

      // Generate Extra Properties

      foreach (EnumProperty ep in EnumInfo.EnumProperties)
      {
        cx.WL("");

        cx.WL("    #region {0}", ep.Name);
        cx.WL("    public {0} {1}", ep.Type, ep.Name);
        cx.WL("    {");
        cx.WL("      get;");
        cx.WL("      protected set;");
        cx.WL("    }");
        cx.WL("    #endregion");
      }
    }
    #endregion

    #region GenerateMethods(IGeneratorContext cx)
    private void GenerateMethods(IGeneratorContext cx)
    {
      cx.WL("");
      cx.WL("    // Public Methods");

      cx.WL("");
      cx.WL("    #region ToString()");
      cx.WL("    public override string ToString()");
      cx.WL("    {");
      cx.WL("      return {0};", EnumInfo.TextPropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region GetHashCode()");
      cx.WL("    public override int GetHashCode()");
      cx.WL("    {");
      cx.WL("      return {0};", EnumInfo.ValuePropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region Equals(object o)");
      cx.WL("    public override bool Equals(object o)");
      cx.WL("    {");
      cx.WL("      if (o.GetType() == typeof(int))");
      cx.WL("        return this.{0} == (int) o;", EnumInfo.ValuePropertyName);
      cx.WL("");
      cx.WL("      if (o.GetType() == typeof(string))");
      cx.WL("        return this.{0} == (string) o;", EnumInfo.TextPropertyName);
      cx.WL("");
      cx.WL("      if (o.GetType() == typeof(" + EnumInfo.Name + "))");
      cx.WL("        return this.{0} == (({1}) o).{0};", EnumInfo.ValuePropertyName, EnumInfo.Name);
      cx.WL("");
      cx.WL("      return false;");
      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateStaticMethods(IGeneratorContext cx)
    private void GenerateStaticMethods(IGeneratorContext cx)
    {
      cx.WL("");
      cx.WL("    // Public Static Methods");

      cx.WL("");
      cx.WL("    #region Parse(string text)");
      cx.WL("    public static {0} Parse(string text)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      if (text == null)");
      cx.WL("        return Unknown;");
      cx.WL("");
      cx.WL("      switch (text.ToLower())");
      cx.WL("      {");

      foreach (Element element in EnumInfo.Elements)
      {
        cx.WL("        case \"{0}\":", element.Name.ToLower());
        cx.WL("          return {0};", element.Name);
        cx.WL("");
      }

      cx.WL("        default:");
      cx.WL("          return Unknown;");
      cx.WL("      }");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region Find(string text)");
      cx.WL("    public static {0} Find(string text)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return Parse(text);");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region Find(int val)");
      cx.WL("    public static {0} Find(int val)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      switch (val)");
      cx.WL("      {");

      foreach (Element element in EnumInfo.Elements)
      {
        cx.WL("        case {0}:", element.Value);
        cx.WL("          return {0};", element.Name);
        cx.WL("");
      }

      cx.WL("        default:");
      cx.WL("          return Unknown;");
      cx.WL("      }");
      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateOperators(IGeneratorContext cx)
    private void GenerateOperators(IGeneratorContext cx)
    {
      cx.WL("");
      cx.WL("    // Public Operators");

      cx.WL("");
      cx.WL("    #region operator {0}(int val)", EnumInfo.Name);
      cx.WL("    public static explicit operator {0}(int val)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return Find(val);");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator {0}(string val)", EnumInfo.Name);
      cx.WL("    public static explicit operator {0}(string val)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return Find(val);");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator int({0} val)", EnumInfo.Name);
      cx.WL("    public static explicit operator int({0} val)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return val.{0};", EnumInfo.ValuePropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator string({0} val)", EnumInfo.Name);
      cx.WL("    public static explicit operator string({0} val)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return val.{0};", EnumInfo.TextPropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator ==({0} lhs, {0} rhs)", EnumInfo.Name);
      cx.WL("    public static bool operator ==({0} lhs, {0} rhs)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return lhs.{0} == rhs.{0};", EnumInfo.ValuePropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator !=({0} lhs, {0} rhs)", EnumInfo.Name);
      cx.WL("    public static bool operator !=({0} lhs, {0} rhs)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return lhs.{0} != rhs.{0};", EnumInfo.ValuePropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator ==({0} lhs, int rhs)", EnumInfo.Name);
      cx.WL("    public static bool operator ==({0} lhs, int rhs)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return lhs.{0} == rhs;", EnumInfo.ValuePropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator !=({0} lhs, int rhs)", EnumInfo.Name);
      cx.WL("    public static bool operator !=({0} lhs, int rhs)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return lhs.{0} != rhs;", EnumInfo.ValuePropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator ==({0} lhs, string rhs)", EnumInfo.Name);
      cx.WL("    public static bool operator ==({0} lhs, string rhs)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return lhs.{0}.ToLower() == rhs.ToLower();", EnumInfo.TextPropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator !=({0} lhs, string rhs)", EnumInfo.Name);
      cx.WL("    public static bool operator !=({0} lhs, string rhs)", EnumInfo.Name);
      cx.WL("    {");
      cx.WL("      return lhs.{0}.ToLower() != rhs.ToLower();", EnumInfo.TextPropertyName);
      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateSubclasses(IGeneratorContext cx)
    private void GenerateSubclasses(IGeneratorContext cx)
    {
      cx.WL("");
      cx.WL("    // Public Classes");

      foreach (Element element in EnumInfo.Elements)
      {
        cx.WL("");
        cx.WL("    #region class {0}{1}", element.Name, EnumInfo.Name);
        cx.WL("    public partial class {0}{1} : {1}", element.Name, EnumInfo.Name);
        cx.WL("    {");

        cx.WL("      // Public Constructors");

        cx.WL("");
        cx.WL("      #region {0}{1}(string text, int val)", element.Name, EnumInfo.Name);
        cx.WL("      public {0}{1}(string text, int val) : base(text, val)", element.Name, EnumInfo.Name);
        cx.WL("      {");
        cx.WL("      }");
        cx.WL("      #endregion");

        if (EnumInfo.EnumProperties.Count > 0)
        {
          string paramDecl = GetParameterDeclaration(cx);
          string paramList = GetParameterList(cx);

          cx.WL("");
          cx.WL("      #region {0}{1}({2})", element.Name, EnumInfo.Name, paramDecl);
          cx.WL("      public {0}{1}({2}) : base({3})", element.Name, EnumInfo.Name, paramDecl, paramList);
          cx.WL("      {");
          cx.WL("      }");
          cx.WL("      #endregion");
        }

        cx.WL("    }");
        cx.WL("    #endregion");
      }
    }
    #endregion

    #region GetParameterDeclaration(IGeneratorContext cx)
    private string GetParameterDeclaration(IGeneratorContext cx)
    {
      StringBuilder sb = new StringBuilder("string text, int val");

      foreach (EnumProperty ep in EnumInfo.EnumProperties)
      {
        sb.Append(", ");
        sb.Append(ep.Type);
        sb.Append(" ");
        sb.Append(ToCamelCase(ep.Name));
      }

      return sb.ToString();
    }
    #endregion

    #region GetParameterList(IGeneratorContext cx)
    private string GetParameterList(IGeneratorContext cx)
    {
      StringBuilder sb = new StringBuilder("text, val");

      foreach (EnumProperty ep in EnumInfo.EnumProperties)
      {
        sb.Append(", ");
        sb.Append(ToCamelCase(ep.Name));
      }

      return sb.ToString();
    }
    #endregion

    #region GetConstructorParams(IGeneratorContext cx, Element element)
    private string GetConstructorParams(IGeneratorContext cx, Element element)
    {
      StringBuilder sb = new StringBuilder();

      sb.Append("\"");
      sb.Append(element.Name);
      sb.Append("\", ");
      sb.Append(element.Value.ToString());

      foreach (EnumProperty ep in EnumInfo.EnumProperties)
      {
        if (element.XmlNode == null ||
            element.XmlNode.Attributes == null ||
            element.XmlNode.Attributes[ep.Name] == null)
        {
          sb.Append(", null");
        }
        else
        {
          XmlAttribute attr = element.XmlNode.Attributes[ep.Name];
          sb.Append(", \"");
          sb.Append(attr.Value);
          sb.Append("\"");
        }
      }

      return sb.ToString();
    }
    #endregion
  }
}
